﻿namespace Mathematics.Expressions
{
    public abstract class Operator:MathExpression
    {
        protected Operator(MathExpression leftTerm,MathExpression rightTerm)
        {
            LeftTerm = leftTerm;
            RightTerm = rightTerm;
        }

        public abstract string OperatorSymbol { get; }
        public MathExpression LeftTerm { get; private set; }
        public MathExpression RightTerm { get; private set; }



        public override ExpressionValue Evaluate()
        {
            var leftDb = LeftTerm.Evaluate();
            var rightDb = RightTerm.Evaluate();

            return PerformOperation(leftDb, rightDb);
        }

        public override string ToString()
        {
            return string.Format("{0} {1} {2}", LeftTerm, OperatorSymbol, RightTerm);
        }


        public override bool CanEvaluate()
        {
            return LeftTerm.CanEvaluate() && RightTerm.CanEvaluate();
        }

        protected abstract ExpressionValue PerformOperation(ExpressionValue left, ExpressionValue right);

        protected internal override MathExpression Reduce(out bool didReduce)
        {
            bool leftDidReduce;
            bool rightDidReduce;
            bool operatorDidReduce;
            var left = LeftTerm.Reduce(out leftDidReduce);
            var right = RightTerm.Reduce(out rightDidReduce);
            var op = ReduceOperator(left, right, out operatorDidReduce);

            didReduce = leftDidReduce || rightDidReduce || operatorDidReduce;

            return op;
        }

        protected  MathExpression ReduceOperator(MathExpression left, MathExpression right, out bool didReduce)
        {
            didReduce = false;
            var leftConstant = left as Constant;
            var rightConstant = right as Constant;
            var leftVariable = left as Variable;
            var rightVariable = right as Variable;

            if (leftConstant != null && rightConstant != null)
            {
                didReduce = true;
                var value = PerformOperation(leftConstant.Value, rightConstant.Value);
                return new Constant(value);
            }

            return this;
        }

    }
}
